package cn.kinoko.common.utils;

import cn.kinoko.common.code.ErrorCodeEnum;
import lombok.Getter;

import java.lang.reflect.Constructor;
import java.security.PrivilegedActionException;
import java.util.Collection;

/**
 * 异常工具类
 *
 * @author kinoko
 * @date 2023/8/2 15:01
 */
public class ExceptionUtil {

    /**
     * 断言，为否则抛出业务异常
     *
     * @param flag 布尔值
     * @param msg  异常信息
     */
    public static void assertion(boolean flag, String msg) {
        if (flag) {
            throw new BizException(msg);
        }
    }

    /**
     * 断言，为否则抛出业务异常
     *
     * @param flag      布尔值
     * @param errorCode 异常code
     */
    public static void assertion(boolean flag, ErrorCodeEnum errorCode) {
        if (flag) {
            throw new BizException(errorCode);
        }
    }

    /**
     * 断言，为否则抛出业务异常
     *
     * @param flag 布尔值
     * @param msg  异常信息
     * @param e    异常类型
     * @param <T>  对象类型
     * @param <E>  异常类型
     */
    public static <T, E extends Exception> void assertion(boolean flag, String msg, Class<E> e) {
        if (flag) {
            throwOut(msg, e);
        }
    }

    /**
     * 判断对象是否为空，为空则抛出业务异常
     *
     * @param obj 对象
     * @param msg 异常信息
     */
    public static void isNull(Object obj, String msg) {
        if (obj != null) {
            return;
        }
        throw new BizException(msg);
    }

    /**
     * 判断对象是否为空，为空则抛出业务异常
     *
     * @param obj       对象
     * @param errorCode 异常code
     */
    public static void isNull(Object obj, ErrorCodeEnum errorCode) {
        if (obj != null) {
            return;
        }
        throw new BizException(errorCode);
    }

    /**
     * 判断对象是否为空，为空则抛出业务异常
     *
     * @param t   对象
     * @param msg 异常信息
     * @param e   异常类型
     * @param <T> 对象类型
     * @param <E> 异常类型
     */
    public static <T, E extends Exception> void isNull(T t, String msg, Class<E> e) {
        if (t != null) {
            return;
        }
        throwOut(msg, e);
    }

    /**
     * 判断对象是否为空，为空则抛出业务异常
     *
     * @param str 字符串
     * @param msg 异常信息
     */
    public static void isBlank(String str, String msg) {
        if (str != null && !str.isEmpty()) {
            return;
        }
        throw new BizException(msg);
    }

    /**
     * 判断对象是否为空，为空则抛出业务异常
     *
     * @param str       字符串
     * @param errorCode 异常code
     */
    public static void isBlank(String str, ErrorCodeEnum errorCode) {
        if (str != null && !str.isEmpty()) {
            return;
        }
        throw new BizException(errorCode);
    }

    /**
     * 判断对象是否为空，为空则抛出业务异常
     *
     * @param str 字符串
     * @param msg 异常信息
     */
    public static <E> void isBlank(String str, String msg, Class<E> e) {
        if (str != null && !str.isEmpty()) {
            return;
        }
        throw new BizException(msg);
    }

    /**
     * 判断集合是否为空，为空则抛出业务异常
     *
     * @param coll    集合
     * @param message 异常信息
     * @param <E>     对象类型
     */
    public static <E> void isEmpty(Collection<E> coll, String message) {
        if (coll != null && !coll.isEmpty()) {
            return;
        }
        throw new BizException(message);
    }

    /**
     * 判断集合是否为空，为空则抛出业务异常
     *
     * @param coll      集合
     * @param errorCode 异常code
     * @param <E>       对象类型
     */
    public static <E> void isEmpty(Collection<E> coll, ErrorCodeEnum errorCode) {
        if (coll != null && !coll.isEmpty()) {
            return;
        }
        throw new BizException(errorCode);
    }

    /**
     * 判断对象是否为空，为空则抛出业务异常
     *
     * @param coll 集合
     * @param msg  异常信息
     * @param e    异常类型
     * @param <T>  对象类型
     * @param <E>  异常类型
     */
    public static <T, E extends Exception> void isEmpty(Collection<T> coll, String msg, Class<E> e) {
        if (coll != null && !coll.isEmpty()) {
            return;
        }
        throwOut(msg, e);
    }

    /**
     * 抛出异常
     *
     * @param msg 异常消息
     * @param e   异常类型
     * @param <E> 异常类型
     */
    public static <E extends Exception> void throwOut(String msg, Class<E> e) {
        try {
            Constructor<E> constructor = e.getConstructor(String.class);
            throw constructor.newInstance(msg);
        } catch (Exception exception) {
            throw new RuntimeException(msg);
        }
    }

    /**
     * 抛出异常
     *
     * @param msg 异常消息
     * @param <E> 异常类型
     */
    public static <E extends Exception> void throwOut(String msg) {
        throw new BizException(msg);
    }

    /**
     * 抛出异常
     *
     * @param errorCode 错误代码
     * @param <E>       异常类型
     */
    public static <E extends Exception> void throwOut(ErrorCodeEnum errorCode) {
        throw new BizException(errorCode);
    }

    /**
     * 业务异常
     */
    @Getter
    public static class BizException extends RuntimeException {

        private ErrorCodeEnum errorCodeEnum;

        public BizException(ErrorCodeEnum errorCodeEnum) {
            super(errorCodeEnum.getDesc());
            this.errorCodeEnum = errorCodeEnum;
        }

        /**
         * Constructs a new exception with {@code null} as its detail message.
         * The cause is not initialized, and may subsequently be initialized by a
         * call to {@link #initCause}.
         */
        public BizException() {
        }

        /**
         * Constructs a new exception with the specified detail message.  The
         * cause is not initialized, and may subsequently be initialized by
         * a call to {@link #initCause}.
         *
         * @param message the detail message. The detail message is saved for
         *                later retrieval by the {@link #getMessage()} method.
         */
        public BizException(String message) {
            super(message);
        }

        /**
         * Constructs a new exception with the specified detail message and
         * cause.  <p>Note that the detail message associated with
         * {@code cause} is <i>not</i> automatically incorporated in
         * this exception's detail message.
         *
         * @param message the detail message (which is saved for later retrieval
         *                by the {@link #getMessage()} method).
         * @param cause   the cause (which is saved for later retrieval by the
         *                {@link #getCause()} method).  (A <tt>null</tt> value is
         *                permitted, and indicates that the cause is nonexistent or
         *                unknown.)
         * @since 1.4
         */
        public BizException(String message, Throwable cause) {
            super(message, cause);
        }

        /**
         * Constructs a new exception with the specified cause and a detail
         * message of <tt>(cause==null ? null : cause.toString())</tt> (which
         * typically contains the class and detail message of <tt>cause</tt>).
         * This constructor is useful for exceptions that are little more than
         * wrappers for other throwables (for example, {@link
         * PrivilegedActionException}).
         *
         * @param cause the cause (which is saved for later retrieval by the
         *              {@link #getCause()} method).  (A <tt>null</tt> value is
         *              permitted, and indicates that the cause is nonexistent or
         *              unknown.)
         * @since 1.4
         */
        public BizException(Throwable cause) {
            super(cause);
        }

        /**
         * Constructs a new exception with the specified detail message,
         * cause, suppression enabled or disabled, and writable stack
         * trace enabled or disabled.
         *
         * @param message            the detail message.
         * @param cause              the cause.  (A {@code null} value is permitted,
         *                           and indicates that the cause is nonexistent or unknown.)
         * @param enableSuppression  whether or not suppression is enabled
         *                           or disabled
         * @param writableStackTrace whether or not the stack trace should
         *                           be writable
         * @since 1.7
         */
        public BizException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }

}